﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.FastTransfer;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace ConAppCore.Test
{
    internal unsafe class TestMarshalEx
    {
        public static void Test()
        {
            //TestBytesToStruct();
            //TestStructCopy();
            //TestPtrCopyPtr();
            //TestStructArrCopy();
            TestStructPtrModifyValue();

            //TestCharCopy();
            //TestShortCopy();
            //TestIntCopy();
            //TestLongCopy();
            //TestFloatCopy();
            //TestDoubleCopy();
            //TestRXX();
        }

        private static void TestRXX()
        {
            int count = 640000;
            Int16[] arr0 = new short[count];
            short value = 0;
            for (int i = 0; i < count; i++)
            {
                arr0[i] = value;
                value++;
            }

            int testCount = 1000;
            Stopwatch stopwatch = new Stopwatch();
            List<double> writeTimes = new List<double>(testCount);

            byte[] buffer1 = new byte[count * 2];
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                using (var ms = new MemoryStream(buffer1))
                {
                    var writer = new BinaryWriter(ms);
                    for (int i = 0; i < count; i++)
                    {
                        writer.Write(arr0[i]);
                    }
                }
                stopwatch.Stop();
                writeTimes.Add(stopwatch.Elapsed.TotalMilliseconds);
            }

            List<double> copyTimes = new List<double>(testCount);
            byte[] buffer2 = new byte[count * 2];
            byte[] tmpBuffer = new byte[buffer2.Length];
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                using (var ms = new MemoryStream(buffer2))
                {
                    var writer = new BinaryWriter(ms);
                    MarshalEx.Copy(arr0, 0, arr0.Length, tmpBuffer, 0);
                    writer.Write(tmpBuffer);
                }
                stopwatch.Stop();
                copyTimes.Add(stopwatch.Elapsed.TotalMilliseconds);
            }

            Int16[] arr2 = new short[count];
            using (var ms = new MemoryStream(buffer2))
            {
                var reader = new BinaryReader(ms);
                for (int i = 0; i < count; i++)
                {
                    arr2[i] = reader.ReadInt16();
                }
            }

            for (int i = 0; i < count; i++)
            {
                if (arr0[i] != arr2[i])
                {

                }
            }

            double writeAvg = Math.Round(writeTimes.Average(), 3);
            double copyAvg = Math.Round(copyTimes.Average(), 3);
            Console.WriteLine($"测试{testCount}次,数据长度:{count},指针写入方式:{copyAvg}ms,流写入方式:{writeAvg}ms");
        }

        private static void TestDoubleCopy()
        {
            int count = 10;
            double[] srcArr = new double[count];
            double value = 0;
            for (int i = 0; i < count; i++)
            {
                srcArr[i] = value;
                value++;
            }

            byte[] buffer = new byte[count * 8 + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, count, buffer, 4);
            }

            double[] srcArr1 = new double[count];
            MarshalEx.Copy(buffer, 4, srcArr1, 0, count);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            double[] srcArr2 = new double[count2];
            for (int i = 0; i < count2; i++)
            {
                srcArr2[i] = reader.ReadDouble();
            }

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }
        private static void TestFloatCopy()
        {
            int count = 10;
            float[] srcArr = new float[count];
            float value = 0;
            for (int i = 0; i < count; i++)
            {
                srcArr[i] = value;
                value++;
            }

            byte[] buffer = new byte[count * 4 + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, count, buffer, 4);
            }

            float[] srcArr1 = new float[count];
            MarshalEx.Copy(buffer, 4, srcArr1, 0, count);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            float[] srcArr2 = new float[count2];
            for (int i = 0; i < count2; i++)
            {
                srcArr2[i] = reader.ReadSingle();
            }

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }
        private static void TestLongCopy()
        {
            int count = 10;
            long[] srcArr = new long[count];
            long value = 0;
            for (int i = 0; i < count; i++)
            {
                srcArr[i] = value;
                value++;
            }

            byte[] buffer = new byte[count * 8 + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, count, buffer, 4);
            }

            long[] srcArr1 = new long[count];
            MarshalEx.Copy(buffer, 4, srcArr1, 0, count);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            long[] srcArr2 = new long[count2];
            for (int i = 0; i < count2; i++)
            {
                srcArr2[i] = reader.ReadInt64();
            }

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }

        private static void TestIntCopy()
        {
            int count = 10;
            int[] srcArr = new int[count];
            int value = 0;
            for (int i = 0; i < count; i++)
            {
                srcArr[i] = value;
                value++;
            }

            byte[] buffer = new byte[count * 4 + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, count, buffer, 4);
            }

            int[] srcArr1 = new int[count];
            MarshalEx.Copy(buffer, 4, srcArr1, 0, count);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            int[] srcArr2 = new int[count2];
            for (int i = 0; i < count2; i++)
            {
                srcArr2[i] = reader.ReadInt32();
            }

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }
        private static void TestShortCopy()
        {
            int count = 10;
            short[] srcArr = new short[count];
            short value = 0;
            for (int i = 0; i < count; i++)
            {
                srcArr[i] = value;
                value++;
            }

            byte[] buffer = new byte[count * 2 + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, count, buffer, 4);
            }

            short[] srcArr1 = new short[count];
            MarshalEx.Copy(buffer, 4, srcArr1, 0, count);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            short[] srcArr2 = new short[count2];
            for (int i = 0; i < count2; i++)
            {
                srcArr2[i] = reader.ReadInt16();
            }

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }

        private static void TestCharCopy()
        {
            char[] srcArr = new char[]
            {
                'a','b','c','d','e',
                'f','g','h','i','j'
            };
            int count = srcArr.Length;
            //char value = char.MinValue;
            byte[] buffer0 = new byte[count + 4];
            using var msW = new MemoryStream(buffer0);
            var writer = new BinaryWriter(msW);
            writer.Write(count);
            writer.Write(srcArr, 0, count);

            //for (int i = 0; i < count; i++)
            //{
            //    writer.Write(value);
            //    srcArr[i] = value;
            //    value++;
            //}

            byte[] buffer = new byte[count + 4];
            fixed (byte* dstP = buffer)
            {
                *(int*)(dstP) = count;
                MarshalEx.Copy(srcArr, 0, srcArr.Length, buffer, 4, Encoding.ASCII);
            }

            for (int i = 0; i < buffer.Length; i++)
            {
                if (buffer0[i] != buffer[i])
                {

                }
            }

            char[] srcArr1 = new char[count];
            MarshalEx.Copy(buffer, 4, buffer0.Length - 4, srcArr1, 0, count, Encoding.ASCII);

            using var ms = new MemoryStream(buffer);
            var reader = new BinaryReader(ms);
            int count2 = reader.ReadInt32();
            char[] srcArr2 = reader.ReadChars(count2);

            for (int i = 0; i < count; i++)
            {
                if (srcArr[i] != srcArr1[i] || srcArr[i] != srcArr2[i])
                {

                }
            }
        }


        private static void TestStructPtrModifyValue()
        {
            var srcHeaders = new Header()
            {
                Sync = 1,
                Ver = 2,
                Flag = 3,
                Length = 4,
                Number = 5,
                DataType = 6,
                Task = 7
            };
            IntPtr ptr = Marshal.AllocHGlobal(sizeof(Header));
            //Header* srcP = &srcHeaders;
            Marshal.StructureToPtr<Header>(srcHeaders, ptr, true);
            
            Header* headP = (Header*)ptr;
            headP->Sync = 11;
            headP->Ver = 22;
            headP->Flag = 33;
            headP->Length = 44;
            headP->Number = 55;
            headP->DataType = 66;
            headP->Task = 77;

            var retHeaders = Marshal.PtrToStructure<Header>(ptr);
            Marshal.FreeHGlobal(ptr);
            ptr = IntPtr.Zero;
        }


        private static void TestPtrCopyPtr()
        {
            byte[] srcBuffer = new byte[4] { 1, 2, 3, 4 };
            byte[] destBuffer = new byte[4];

            fixed (byte* src = srcBuffer)
            {
                fixed (byte* dest = destBuffer)
                {
                    Buffer.MemoryCopy(src, dest, destBuffer.Length, srcBuffer.Length / 2);
                }
            }

            byte[] destBuffer2 = new byte[4];
            fixed (byte* src = srcBuffer)
            {
                fixed (byte* dest = destBuffer2)
                {
                    MarshalEx.Copy(src, dest, destBuffer.Length);
                }
            }


            IntPtr srcPtr = Marshal.AllocHGlobal(4);
            Marshal.Copy(srcBuffer, 0, srcPtr, srcBuffer.Length);

            IntPtr destPtr = Marshal.AllocHGlobal(4);
            MarshalEx.Copy(srcPtr, destPtr, destBuffer.Length);

            byte[] destBuffer3 = new byte[4];
            Marshal.Copy(destPtr, destBuffer3, 0, destBuffer3.Length);
            Marshal.FreeHGlobal(srcPtr);
            Marshal.FreeHGlobal(destPtr);
        }



        private static void TestStructArrCopy()
        {
            Header[] headers = new Header[3];
            headers[0] = new Header()
            {
                Sync = 1,
                Ver = 2,
                Flag = 3,
                Length = 4,
                Number = 5,
                DataType = 6,
                Task = 7
            };
            headers[1] = new Header()
            {
                Sync = 2,
                Ver = 4,
                Flag = 6,
                Length = 8,
                Number = 10,
                DataType = 12,
                Task = 14
            };
            headers[2] = new Header()
            {
                Sync = 3,
                Ver = 6,
                Flag = 9,
                Length = 12,
                Number = 15,
                DataType = 18,
                Task = 21
            };

            int size = sizeof(Header);
            byte[] buffer = new byte[size * 3];
            fixed (Header* src = headers)
            {
                MarshalEx.Copy(src, buffer.Length, buffer, 0);
            }

            Header[] headers2 = new Header[3];

            using (var ms = new MemoryStream(buffer))
            {
                var reader = new BinaryReader(ms);
                for (int i = 0; i < headers.Length; i++)
                {
                    Header header1 = new Header();
                    header1.Sync = reader.ReadUInt32();
                    header1.Ver = reader.ReadByte();
                    header1.Flag = reader.ReadByte();
                    header1.Length = reader.ReadInt32();
                    header1.Number = reader.ReadInt16();
                    header1.DataType = reader.ReadInt16();
                    header1.Task = reader.ReadUInt32();
                    headers2[i] = header1;
                }
            }

            Header[] headers3 = new Header[3];
            fixed (Header* dst3 = headers3)
            {
                MarshalEx.Copy(buffer, 0, dst3, buffer.Length);
            }
        }

        private static void TestStructCopy()
        {
            Header header0 = new Header()
            {
                Sync = 0xF99FEFFE,
                Ver = 1,
                Flag = 0,
                Length = 6400,
                Number = 23,
                DataType = 25,
                Task = 101
            };

            int size = sizeof(Header);
            byte[] buffer = new byte[size];
            MarshalEx.Copy(&header0, size, buffer, 0);

            Header header2 = new Header();
            MarshalEx.Copy(buffer, 0, &header2, size);

            Header header1 = new Header();
            using (var ms = new MemoryStream(buffer))
            {
                var reader = new BinaryReader(ms);
                header1.Sync = reader.ReadUInt32();
                header1.Ver = reader.ReadByte();
                header1.Flag = reader.ReadByte();
                header1.Length = reader.ReadInt32();
                header1.Number = reader.ReadInt16();
                header1.DataType = reader.ReadInt16();
                header1.Task = reader.ReadUInt32();
            }
        }

        private static void TestBytesToStruct()
        {
            Header header0 = new Header()
            {
                Sync = 0xF99FEFFE,
                Ver = 1,
                Flag = 0,
                Length = 6400,
                Number = 23,
                DataType = 25,
                Task = 101
            };

            int size = sizeof(Header);
            //MemoryMarshal.Write()

            //方法一:
            byte[] buffer1 = new byte[size];
            Header* srcP = &header0;
            nint srcPtr = (nint)(srcP);
            Marshal.Copy(srcPtr, buffer1, 0, size);

            //GCHandle handle = GCHandle.Alloc(header0, GCHandleType.Pinned);
            //IntPtr pppp = handle.AddrOfPinnedObject();
            //void* pp = &header0;
            //handle.Free();


            Header header12 = new Header();
            Header* srcP2 = &header12;
            nint srcPtr2 = (nint)(srcP2);
            Marshal.Copy(buffer1, 0, srcPtr2, size);

            Header header1 = new Header();
            using (var ms = new MemoryStream(buffer1))
            {
                var reader = new BinaryReader(ms);
                header1.Sync = reader.ReadUInt32();
                header1.Ver = reader.ReadByte();
                header1.Flag = reader.ReadByte();
                header1.Length = reader.ReadInt32();
                header1.Number = reader.ReadInt16();
                header1.DataType = reader.ReadInt16();
                header1.Task = reader.ReadUInt32();
            }

            //方法二:
            byte[] buffer2 = new byte[size];
            Header header22 = new Header();
            fixed (byte* dstP = buffer2)
            {
                nint dstPtr = (nint)(dstP);
                Marshal.StructureToPtr<Header>(header0, dstPtr, true);

                Marshal.PtrToStructure<Header>(dstPtr, header22);
            }

            Header header2 = new Header();
            using (var ms = new MemoryStream(buffer2))
            {
                var reader = new BinaryReader(ms);
                header2.Sync = reader.ReadUInt32();
                header2.Ver = reader.ReadByte();
                header2.Flag = reader.ReadByte();
                header2.Length = reader.ReadInt32();
                header2.Number = reader.ReadInt16();
                header2.DataType = reader.ReadInt16();
                header2.Task = reader.ReadUInt32();
            }

        }


        [StructLayout(LayoutKind.Explicit, Size = 18), UnsafeValueType, CompilerGenerated]
        private struct Header
        {
            [FieldOffset(0)]
            public uint Sync;

            [FieldOffset(4)]
            public byte Ver;

            [FieldOffset(5)]
            public byte Flag;

            [FieldOffset(6)]
            public int Length;

            [FieldOffset(10)]
            public short Number;

            [FieldOffset(12)]
            public short DataType;

            [FieldOffset(14)]
            public uint Task;
        }

    }
}
